package bsh.meta;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.objectweb.asm.Type;

/**
 * Representa um tipo. Equivalente ao Class, exceto que pode ser usado para classes em processo de compilacao
 * 
 * @author takeshi
 */
public interface TypeWrapper {

    /**
     * Devolve uma lista com o tipo dos parametros dos contrutores
     * 
     * @return {@link Collection} de {@link List} de {@link TypeWrapper}
     */
    Collection<? extends List<TypeWrapper>> visibleConstructors();

    /**
     * @return {@link Collection} of the inner types declared by this type
     */
    Collection<? extends TypeWrapper> getInnerTypes();

    /**
     * @return the enclosing type if this type represents an inner type.
     */
    TypeWrapper getEnclosingType();

    /**
     * @return boolean
     */
    boolean isInnerClass();

    /**
     * @return {@link Collection} de {@link FieldDescriptor}
     */
    Collection<? extends FieldDescriptor> visibleFields();

    /**
     * @return {@link Collection} de {@link FieldDescriptor}
     */
    Collection<? extends FieldDescriptor> protectedFields();

    /**
     * @return {@link Collection} de {@link MethodDescriptor}
     */
    Collection<? extends MethodDescriptor> visibleMethods();

    /**
     * @return {@link Collection} de {@link MethodDescriptor}
     */
    Collection<? extends MethodDescriptor> protectedMethods();

    /**
     * Deve devolver o 'internal name' como definido pela JLS. Normalmente, basta trocar o '.' por '/' no FQCN, exceto para primitivas
     * 
     * @return String
     */
    String internalName();

    /**
     * Devolve o descritor de tipo. Deve ser usado para descrever assinaturas de metodos.
     * 
     * @return String
     */
    String typeDescription();

    /**
     * deve devolver o FQCN
     * 
     * @return String
     */
    String className();

    /**
     * @return the simple name of this class, if any
     */
    String simpleName();

    /**
     * devolve o super tipo
     * 
     * @return {@link TypeWrapper}
     */
    TypeWrapper superType();

    /**
     * Devolve as interfaces implementadas por esta classe ou as super interfaces se esta classe for uma interface
     * 
     * @return {@link Collection} de {@link TypeWrapper}
     */
    List<? extends TypeWrapper> interfaces();

    /**
     * @return boolean
     */
    boolean isInterface();

    /**
     * @return boolean
     */
    boolean isPrimitive();

    /**
     * @return converte para um {@link Type} do ASM
     */
    Type toASMType();

    /**
     * Devolve o {@link FieldDescriptor}
     * 
     * @param name String
     * @return {@link FieldDescriptor}
     */
    FieldDescriptor getField(String name);

    /**
     * Verifica se esta classe eh uma superclasse ou super interface da classe passada
     * 
     * @param another {@link TypeWrapper}
     * @return boolean
     */
    boolean isSuperTypeOf(TypeWrapper another);

    TypeWrapper NULL_TYPE_WRAPPER = new AbstractTypeWrapper() {

        public String className() {
            return "null";
        }

        public List<? extends TypeWrapper> interfaces() {
            return Collections.emptyList();
        }

        public String internalName() {
            return "null";
        }

        public boolean isInterface() {
            return false;
        }

        public TypeWrapper superType() {
            return null;
        }

        public String typeDescription() {
            return "null";
        }

        public Collection<? extends FieldDescriptor> visibleFields() {
            return JavaClassWrapper.OBJECT_WRAPPER.visibleFields();
        }

        public Collection<? extends MethodDescriptor> visibleMethods() {
            return JavaClassWrapper.OBJECT_WRAPPER.visibleMethods();
        }

        @Override
        public Type toASMType() {
            return JavaClassWrapper.OBJECT_WRAPPER.toASMType();
        }

        public Collection<? extends List<TypeWrapper>> visibleConstructors() {
            return Collections.emptyList();
        }

        public Collection<? extends FieldDescriptor> protectedFields() {
            return JavaClassWrapper.OBJECT_WRAPPER.protectedFields();
        }

        public Collection<? extends MethodDescriptor> protectedMethods() {
            return JavaClassWrapper.OBJECT_WRAPPER.protectedMethods();
        }

        public String toString() {
            return "NULL";
        }

        public Collection<? extends TypeWrapper> getInnerTypes() {
            return Collections.emptyList();
        }

        public TypeWrapper getEnclosingType() {
            return null;
        }

        public boolean isStatic() {
            return true;
        }

    };

    /**
     * Devolve o numero de 'registradores' usados por instancias desta classe. Deve devolver 2 para long e double (primitivas) e 1 para
     * todos os outros casos.
     * 
     * @return Int
     */
    int numberOfSlots();

    /**
     * @return boolean
     */
    boolean isStatic();

}
